﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __Report__

    public class PublicationCountByMonthReport : ReportOperation
    {
        #region Properties

        public override string MimeType
        {
            get
            {
                return "text/xml";
            }
        }

        #endregion

        #region Utilities

        protected override void Report(IUnitOfWork uow)
        {
            var dtNow = DateTime.UtcNow;
            int? authorID = null;
            int? languageID = null;
            int year = dtNow.Year;
            int month = dtNow.Month;
            int day = dtNow.Day;
            int? period = null;
            var flagMask = PublicationFlags.Active;

            if(Properties.ContainsKey("authorID"))
                authorID = Properties["authorID"].ParseAs<Int32?>();
            if(Properties.ContainsKey("languageID"))
                languageID = Properties["languageID"].ParseAs<Int32?>();
            if(Properties.ContainsKey("year"))
                year = Properties["year"].ParseAs(year);
            if(Properties.ContainsKey("month"))
                month = Properties["month"].ParseAs(month);
            if(Properties.ContainsKey("day"))
                day = Properties["day"].ParseAs(day);
            if(Properties.ContainsKey("period"))
                period = Properties["period"].ParseAs<Int32?>();
            if(Properties.ContainsKey("flagMask"))
                flagMask = Properties["flagMask"].ParseAs<PublicationFlags>();

            var dtTo = new DateTime(year, month, day);
            var dtFrom = period.HasValue 
                ? dtTo.AddMonths(-period.Value)
                : (DateTime?)null;
            dtTo = dtTo.AddHours(dtNow.Hour);

            var query = uow.Query<PublicationEntity>()
                .FilterByFlags(x => (int)x.Flags, (int)flagMask)
                .FilterByDateRange(x => x.PublishedOn, dtFrom, dtTo);
            if(authorID.HasValue)
                query = query.Where(x => x.Author.EntityID == authorID.Value);
            if(languageID.HasValue)
                query = query.Where(x => x.Translations.Any(i => i.Language.EntityID == languageID.Value));

            var data = query.Select(x => x.PublishedOn).ToList();
            var xReport = new XElement("report");
            if(data.Any())
            {
                var tmp = data.GroupBy(x => String.Format("{0}-{1}", x.Year, x.Month))
                    .ToDictionary(x => x.Key, x => x.Count());
                var dtMin = dtFrom.HasValue 
                    ? dtFrom.Value 
                    : data.Min().Date;
                var dt = new DateTime(dtMin.Year, dtMin.Month, 1);
                while(dt <= dtTo)
                {
                    string key = String.Format("{0}-{1}", dt.Year, dt.Month);
                    int count = 0;
                    if(tmp.ContainsKey(key))
                        count = tmp[key];
                    var xEntry = new XElement("entry",
                        new XAttribute("dt", dt.ToBinary()),
                        new XAttribute("count", count));
                    xReport.Add(xEntry);
                    dt = dt.AddMonths(1);
                }
            }
            xReport.Save(OutStream);
        }

        #endregion
    }

    public class TopNForumMemberByKarmaPointsReport : ReportOperation
    {
        #region Properties

        public override string MimeType
        {
            get
            {
                return "text/xml";
            }
        }

        #endregion

        #region Utilities

        protected override void Report(IUnitOfWork uow)
        {
            int num = 10;
            if(Properties.ContainsKey("num"))
                num = Properties["num"].ParseAs(10);

            var query = uow.Query<ForumMemberEntity>()
                .FilterByFlags(x => (int)x.Flags, (int)ForumMemberFlags.Active)
                .Where(x => x.KarmaPoints != 0)
                .OrderBy(x => x.KarmaPoints, SortDirection.Descending)
                .Take(num);
            var data = query.ToList();
            var xReport = new XElement("report");
            if(data.Any())
            {
                foreach(var i in data)
                {
                    xReport.Add(new XElement("entry",
                        new XAttribute("entityID", i.EntityID),
                        new XAttribute("topicCount", i.TopicCount),
                        new XAttribute("postCount", i.PostCount),
                        new XAttribute("karmaPoints", i.KarmaPoints),
                        new XAttribute("avatarFileID", i.AvatarFileID),
                        new XAttribute("avatarFileGuid", i.AvatarFile != null ? i.AvatarFile.Guid : Guid.Empty), 
                        new XAttribute("accountID", i.Account.EntityID), 
                        new XAttribute("accountUsername", i.Account.Username),
                        new XAttribute("accountEmail", i.Account.Email)));
                }
            }
            xReport.Save(OutStream);
        }

        #endregion
    }

    public class TopNForumMemberByPostCountReport : ReportOperation
    {
        #region Properties

        public override string MimeType
        {
            get
            {
                return "text/xml";
            }
        }

        #endregion

        #region Utilities

        protected override void Report(IUnitOfWork uow)
        {
            int num = 10;
            if(Properties.ContainsKey("num"))
                num = Properties["num"].ParseAs(10);

            var query = uow.Query<ForumMemberEntity>()
                .FilterByFlags(x => (int)x.Flags, (int)ForumMemberFlags.Active)
                .Where(x => x.PostCount != 0)
                .OrderBy(x => x.PostCount, SortDirection.Descending)
                .Take(num);
            var data = query.ToList();
            var xReport = new XElement("report");
            if(data.Any())
            {
                foreach(var i in data)
                {
                    xReport.Add(new XElement("entry",
                        new XAttribute("entityID", i.EntityID),
                        new XAttribute("topicCount", i.TopicCount),
                        new XAttribute("postCount", i.PostCount),
                        new XAttribute("karmaPoints", i.KarmaPoints),
                        new XAttribute("avatarFileID", i.AvatarFileID),
                        new XAttribute("avatarFileGuid", i.AvatarFile != null ? i.AvatarFile.Guid : Guid.Empty),
                        new XAttribute("accountID", i.Account.EntityID),
                        new XAttribute("accountUsername", i.Account.Username),
                        new XAttribute("accountEmail", i.Account.Email)));
                }
            }
            xReport.Save(OutStream);
        }

        #endregion
    }

    public class ForumMembersOnlineReport : ReportOperation
    {
        #region Properties

        public override string MimeType
        {
            get
            {
                return "text/xml";
            }
        }

        #endregion

        #region Utilities

        protected override void Report(IUnitOfWork uow)
        {
            int num = 10;
            if(Properties.ContainsKey("num"))
                num = Properties["num"].ParseAs(10);

            var dtNow = DateTime.UtcNow;
            var sessionUpdatePeriod = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Core").Of<ConnectToSession>().Name("UpdatePeriod")
            }).ResultAs(5);
            var accessedOn = dtNow.AddMinutes(-sessionUpdatePeriod * 5);
            var query = uow.Query<ForumMemberEntity>()
                .FilterByFlags(x => (int)x.Flags, (int)ForumMemberFlags.Active)
                .Where(x => x.Account.Sessions.Any(i => accessedOn <= i.AccessedOn))
                .OrderBy(x => x.Account.Username, SortDirection.Ascending)
                .Take(num);
            var data = query.ToList();
            var xReport = new XElement("report");
            if(data.Any())
            {
                foreach(var i in data)
                {
                    xReport.Add(new XElement("entry",
                        new XAttribute("entityID", i.EntityID),
                        new XAttribute("topicCount", i.TopicCount),
                        new XAttribute("postCount", i.PostCount),
                        new XAttribute("karmaPoints", i.KarmaPoints),
                        new XAttribute("avatarFileID", i.AvatarFileID),
                        new XAttribute("avatarFileGuid", i.AvatarFile != null ? i.AvatarFile.Guid : Guid.Empty),
                        new XAttribute("accountID", i.Account.EntityID),
                        new XAttribute("accountUsername", i.Account.Username),
                        new XAttribute("accountEmail", i.Account.Email)));
                }
            }
            xReport.Save(OutStream);
        }

        #endregion
    }

    public class ActivityReport : ReportOperation
    {
        #region Properties

        public override string MimeType
        {
            get
            {
                return "text/xml";
            }
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(Properties == null)
                return Err.Of(this).No("Properties");
            if(!Properties.ContainsKey("languageID"))
                return Err.Of(this).NotSpecified("language");
            return base.Validate(uow);
        }

        protected override void Report(IUnitOfWork uow)
        {
            int languageID = Properties["languageID"].ParseAs<Int32>();
            int accountID = 0;
            if(Properties.ContainsKey("accountID")) 
                accountID = Properties["accountID"].ParseAs<Int32>();
            int num = 100;
            if(Properties.ContainsKey("num"))
                num = Properties["num"].ParseAs(100);

            var entries = new Dictionary<DateTime, Dictionary<String, String>>();
            entries.AddValues(uow.Query<ForumTopicEntity>()
                .Where(x => accountID == 0 || x.Member.Account.EntityID == accountID)
                .OrderByDescending(x => x.CreatedOn)
                .Take(num)
                .ToDictionary(x => x.CreatedOn, x => new Dictionary<String, String>
                {
                    {"Type", "ForumTopic"},
                    {"EntityID", x.EntityID.ToString()},
                    {"Title", x.Title},
                    {"Description", x.Description}
                }));
            entries.AddValues(uow.Query<ForumPostEntity>()
                .Where(x => accountID == 0 || x.Member.Account.EntityID == accountID)
                .OrderByDescending(x => x.CreatedOn)
                .Take(num)
                .ToDictionary(x => x.CreatedOn, x => new Dictionary<String, String>
                {
                    {"Type", "ForumPost"},
                    {"EntityID", x.Topic.EntityID.ToString()},
                    {"Title", x.Topic.Title},
                    {"Description", x.Text}
                }));
            entries.AddValues(uow.Query<PublicationEntity>()
                .Where(x => accountID == 0 || x.Author.Account.EntityID == accountID)
                .Where(x => x.Translations.Any(i => i.Language.EntityID == languageID))
                .FilterByFlags(x => (int)x.Flags, 
                    (int)(PublicationFlags.Active | 
                        PublicationFlags.BlogPost | 
                        PublicationFlags.News | 
                        PublicationFlags.Article))
                .FilterByDateRange(x => x.PublishedOn, null, DateTime.UtcNow)
                .OrderByDescending(x => x.PublishedOn)
                .Take(num)
                .ToDictionary(x => x.CreatedOn, x => new Dictionary<String, String>
                {
                    {"Type", "Publication_" + FlagsHelper.G1_Flags(x.Flags).ToString()},
                    {"EntityID", x.EntityID.ToString()},
                    {"Title", x.Translation(languageID).Title},
                    {"Description", x.Translation(languageID).Description}
                }));
            var xReport = new XElement("report");
            foreach(var i in entries.OrderByDescending(x => x.Key).Take(100))
            {
                var xEntry = new XElement("entry", new XAttribute("Date", i.Key.ToBinary()));
                xEntry.Add(i.Value.Select(x => new XAttribute(x.Key, x.Value)).ToArray());
                xReport.Add(xEntry);
            }
            xReport.Save(OutStream);
        }

        #endregion
    }

    #endregion

    #region __Notification__

    public class EnqueueContactMessage : Operation<MessageEntity>,
        IApiOperation
    {
        #region Properties

        public int LanguageID
        {
            get;
            set;
        }

        public string ReplyTo
        {
            get;
            set;
        }

        public string Subject
        {
            get;
            set;
        }

        public string Text
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "CONTACTMSG",
                LanguageID = LanguageID,
                Replacements = new Dictionary<String, String>
                {
                    {"Message.ReplyTo", ReplyTo},
                    {"Message.Subject", Subject},
                    {"Message.Text", Text}
                },
                ReplyTo = ReplyTo,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    #endregion
}
